package cn.yh.nft.turbo.chain.domain.service;

import cn.hutool.core.lang.Chain;
import cn.yh.nft.turbo.api.chain.constant.ChainOperateBizTypeEnum;
import cn.yh.nft.turbo.api.chain.constant.ChainOperateTypeEnum;
import cn.yh.nft.turbo.api.chain.constant.ChainType;
import cn.yh.nft.turbo.api.chain.model.ChainOperateBody;
import cn.yh.nft.turbo.api.chain.request.ChainProcessRequest;
import cn.yh.nft.turbo.api.chain.request.ChainQueryRequest;
import cn.yh.nft.turbo.api.chain.response.ChainProcessResponse;
import cn.yh.nft.turbo.api.chain.response.data.ChainCreateData;
import cn.yh.nft.turbo.api.chain.response.data.ChainOperationData;
import cn.yh.nft.turbo.api.chain.response.data.ChainResultData;
import cn.yh.nft.turbo.base.exception.RepoErrorCode;
import cn.yh.nft.turbo.base.exception.SystemException;
import cn.yh.nft.turbo.base.utils.BeanValidator;
import cn.yh.nft.turbo.chain.domain.constant.ChainCodeEnum;
import cn.yh.nft.turbo.chain.domain.constant.ChainOperateStateEnum;
import cn.yh.nft.turbo.chain.domain.entity.ChainOperateInfo;
import cn.yh.nft.turbo.chain.domain.entity.ChainRequest;
import cn.yh.nft.turbo.chain.domain.response.ChainResponse;
import cn.yh.nft.turbo.limiter.SlidingWindowRateLimiter;
import cn.yh.nft.turbo.stream.producer.StreamProducer;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import groovy.util.logging.Slf4j;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.checkerframework.checker.units.qual.C;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.concurrent.ThreadFactory;
import java.util.function.Function;

import static cn.yh.nft.turbo.api.chain.constant.ChainOperateTypeEnum.COLLECTION_DESTROY;
import static java.util.Objects.requireNonNull;

/**
 * @ClassName: AbstractChainService
 * @Author: 草莓爱上啵啵
 */
@lombok.extern.slf4j.Slf4j
@Slf4j
public abstract class AbstractChainService implements ChainService{

    @Autowired
    protected ChainOperateInfoService chainOperateInfoService;

    @Autowired
    protected SlidingWindowRateLimiter slidingWindowRateLimiter;


    @Autowired
    private StreamProducer streamProducer;


    private static ThreadFactory chainResultProcessFactory = new ThreadFactoryBuilder()
            .setNameFormat("chain-result-process-pool-%d").build();

    ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(10, chainResultProcessFactory);

    /**
     * 执行链式操作的POST请求处理方法
     *
     * @param chainProcessRequest 链式操作请求对象，包含业务类型、业务ID、标识符等信息
     * @param chainOperateTypeEnum 链式操作类型枚举，表示不同的操作类型
     * @param consumer 消费者函数式接口，用于执行核心链式操作逻辑
     * @return 返回链式操作处理响应对象，包含处理结果、数据等信息
     */
    protected ChainProcessResponse doPostExecute(ChainProcessRequest chainProcessRequest, ChainOperateTypeEnum chainOperateTypeEnum,
                                                 Consumer<ChainRequest> consumer){

        return handle(chainProcessRequest, request -> {

            // 尝试获取滑动窗口速率限制器的令牌
            Boolean rateLimitResult = slidingWindowRateLimiter.tryAcquire("limit#" + chainProcessRequest.getBizType() + chainProcessRequest.getIdentifier(), 1, 60);
            // 如果速率限制失败，返回处理中的响应
            if (!rateLimitResult){
                return new ChainProcessResponse.Builder().responseCode(ChainCodeEnum.PROCESSING.name()).data(
                        new ChainOperationData(chainProcessRequest.getIdentifier())).buildSuccess();
            }

            // 查询链式操作信息
            ChainOperateInfo chainOperateInfo = chainOperateInfoService
                    .queryByOutBizId(chainProcessRequest.getBizId(), chainProcessRequest.getBizType(), chainProcessRequest.getIdentifier());

            // 如果链式操作信息已存在，返回重复响应
            if (ObjectUtils.isNotEmpty(chainOperateInfo)){
                return duplicateResponse(chainProcessRequest, chainOperateInfo);
            }

            // 创建链式请求对象
            ChainRequest chainRequest = new ChainRequest();

            // 插入链式操作信息记录
            var operateInfoId = chainOperateInfoService.insertInfo(chainType(),
                    chainProcessRequest.getBizId(), chainProcessRequest.getBizType(), chainOperateTypeEnum.name(),
                    JSON.toJSONString(chainProcessRequest), chainProcessRequest.getIdentifier());
            // 核心逻辑执行
            consumer.accept(chainRequest);

            // 执行链式操作并获取响应结果
            ChainResponse result =doPost(chainRequest);
            log.info("wen chang post result:{}", JSON.toJSONString(result));

            // 更新链式操作信息的结果
            boolean updateResult = chainOperateInfoService.updateResult(operateInfoId, null,
                    result.getSuccess() ? result.getData().toString() : result.getError().toString());

            // 如果更新结果失败，抛出系统异常
            if (!updateResult){
                throw new SystemException(RepoErrorCode.UPDATE_FAILED);
            }

            // 构建链式操作处理响应
            ChainProcessResponse response = buildResult(result, chainProcessRequest, chainOperateTypeEnum);

            // 如果操作成功且操作类型不是用户创建，则延迟发送状态通知
            if (response.getSuccess() && chainOperateTypeEnum != ChainOperateTypeEnum.USER_CREATE) {
                //延迟5秒之后查询状态并发送MQ消息通知上游
                scheduler.schedule( () -> {
                    try {
                        // 再次查询链式操作信息
                        ChainOperateInfo operateInfo = chainOperateInfoService.queryByOutBizId(chainProcessRequest.getBizId(), chainProcessRequest.getBizType(),
                                chainProcessRequest.getIdentifier());
                        // 查询链式操作结果
                        ChainProcessResponse<ChainResultData> queryChainResult = queryChainResult(
                                new ChainQueryRequest(chainProcessRequest.getIdentifier(), operateInfoId.toString()));

                        // 如果查询成功且数据不为空，更新操作状态并发送通知
                        if (queryChainResult.getSuccess() && queryChainResult.getData() != null){
                            if (StringUtils.equals(queryChainResult.getData().getState(), ChainOperateStateEnum.SUCCEED.name())){
                                this.sendMsg(operateInfo, queryChainResult.getData());

                                // 更新链式操作信息的状态为成功
                                chainOperateInfoService.updateResult(operateInfoId,
                                        ChainOperateStateEnum.SUCCEED, null);
                            }
                        }

                    }catch (Exception e){
                        log.error("query chain result failed,", e);
                    }
                } , 5, TimeUnit.SECONDS);
            }
            return response;
        });

    }

    /**
     * 根据请求类型复制或构建响应对象
     *
     * 当处理用户创建类型的业务时，此方法解析操作结果以获取区块链地址信息，
     * 并结合请求信息构建一个成功的响应对象对于其他业务类型，构建一个表示
     * 正在处理状态的响应对象
     *
     * @param chainProcessRequest 包含业务类型和用户ID等信息的请求对象
     * @param chainOperateInfo 包含操作结果信息的对象
     * @return 根据业务类型和操作结果构建的响应对象
     */
    private ChainProcessResponse duplicateResponse(ChainProcessRequest chainProcessRequest, ChainOperateInfo chainOperateInfo){
        // 检查请求的业务类型是否为用户创建
        if (StringUtils.equals(chainProcessRequest.getBizType(), ChainOperateTypeEnum.USER_CREATE.name())){
            // 解析操作结果为JSON对象以获取区块链地址
            JSONObject jsonObject = JSON.parseObject(chainOperateInfo.getResult(),JSONObject.class);
            String blockChainAddr = (String) jsonObject.get("native_address");
            // 使用请求的用户ID作为区块链名称
            String blockChainName = chainProcessRequest.getUserId();
            // 构建包含区块链地址和名称的成功响应对象
            return new ChainProcessResponse.Builder().responseCode(ChainCodeEnum.SUCCESS.name()).data(
                    new ChainCreateData(chainProcessRequest.getIdentifier(), blockChainAddr, blockChainName, chainType())
            ).buildSuccess();
        } else {
            // 对于其他业务类型，构建表示正在处理状态的响应对象
            return new ChainProcessResponse.Builder().responseCode(ChainCodeEnum.PROCESSING.name()).data(
                    new ChainOperationData(chainProcessRequest.getIdentifier())
            ).buildSuccess();
        }
    }

    /**
     * 执行删除操作的链式处理方法
     *
     * 该方法负责处理删除请求，包括速率限制、操作信息查询、插入操作信息、执行核心删除逻辑、
     * 更新操作结果以及异步查询状态并通知上游系统
     *
     * @param chainProcessRequest 链式处理请求对象，包含业务类型、业务ID、标识符等信息
     * @param consumer 消费者函数式接口，用于执行核心删除逻辑
     * @return 返回链式处理响应对象，包含处理结果和数据
     */
    protected ChainProcessResponse doDeleteExecute(ChainProcessRequest chainProcessRequest, Consumer<ChainRequest> consumer){

        return handle(chainProcessRequest, request -> {

            //执行速率限制，防止过多请求
            Boolean rateLimitResult = slidingWindowRateLimiter.tryAcquire(
                    "limit#" + chainProcessRequest.getBizType() + chainProcessRequest.getIdentifier(), 1, 60);

            //如果速率限制失败，返回处理中的响应
            if (!rateLimitResult) {
                return new ChainProcessResponse.Builder().responseCode(ChainCodeEnum.PROCESSING.name()).data(
                        new ChainOperationData(chainProcessRequest.getIdentifier())).buildSuccess();
            }

            //查询链式操作信息
            ChainOperateInfo chainOperateInfo = chainOperateInfoService.queryByOutBizId(chainProcessRequest.getBizId(), chainProcessRequest.getBizType(),
                    chainProcessRequest.getIdentifier());
            //如果操作信息已存在，返回处理中的响应
            if (null != chainOperateInfo){
                return new ChainProcessResponse.Builder().responseCode(ChainCodeEnum.PROCESSING.name()).data(
                        new ChainOperationData(chainProcessRequest.getIdentifier())).buildSuccess();
            }

            //创建链式请求对象
            ChainRequest chainRequest = new ChainRequest();

            //插入链式操作信息
            var operateInfoId = chainOperateInfoService.insertInfo(chainType(),
                    chainProcessRequest.getBizId(), chainProcessRequest.getBizType(), COLLECTION_DESTROY.name(),
                    JSON.toJSONString(chainProcessRequest), chainProcessRequest.getIdentifier());

            //核心逻辑执行
            consumer.accept(chainRequest);

            //执行删除操作并获取结果
            ChainResponse result =doDelete(chainRequest);

            log.info("wen chang delete result:{}", result);

            //更新链式操作结果
            boolean updateResult = chainOperateInfoService.updateResult(operateInfoId, null,
                    result.getSuccess() ? result.getData().toString() : result.getError().toString());

            //如果更新结果失败，抛出系统异常
            if (!updateResult){
                throw new SystemException(RepoErrorCode.UPDATE_FAILED);
            }

            //构建链式处理响应
            ChainProcessResponse response = buildResult(result, chainProcessRequest, COLLECTION_DESTROY);

            //如果处理成功，调度异步任务查询状态并发送通知
            if (response.getSuccess()){
                scheduler.schedule( () -> {
                    try{
                        //再次查询链式操作信息
                        ChainOperateInfo operateInfo = chainOperateInfoService.queryByOutBizId(chainProcessRequest.getBizId(), chainProcessRequest.getBizType(),
                                chainProcessRequest.getIdentifier());
                        //查询链式操作结果
                        ChainProcessResponse<ChainResultData> queryChainResult = queryChainResult(
                                new ChainQueryRequest(chainProcessRequest.getIdentifier(), operateInfoId.toString()));

                        //如果查询成功且状态为成功，发送消息并更新状态
                        if (queryChainResult.getSuccess() && queryChainResult.getData() != null){
                            if (StringUtils.equals(queryChainResult.getData().getState(), ChainOperateStateEnum.SUCCEED.name())){
                                this.sendMsg(operateInfo, queryChainResult.getData());

                                chainOperateInfoService.updateResult(operateInfoId,
                                        ChainOperateStateEnum.SUCCEED, null);
                            }
                        }
                    }catch (Exception e){
                        log.error("query chain result failed,", e);
                    }
                },5, TimeUnit.SECONDS);
            }

            //返回链式处理响应
            return response;
        });
    }


    /**
     * 结果构造
     * 根据不同的操作类型和操作结果，构建相应的处理响应
     * @param result 链操作结果，包含操作是否成功、响应码、响应消息等信息
     * @param chainProcessRequest 链处理请求，包含请求的标识符、用户ID等信息
     * @param chainOperateTypeEnum 链操作类型枚举，表示当前操作的类型
     * @return 返回链处理的响应，根据操作结果和类型构建
     */
    private ChainProcessResponse buildResult(ChainResponse result, ChainProcessRequest chainProcessRequest, ChainOperateTypeEnum chainOperateTypeEnum) {
        // 如果链操作成功
        if (result.getSuccess()){
            // 如果操作类型是用户创建
            if (chainOperateTypeEnum == ChainOperateTypeEnum.USER_CREATE){
                // 解析结果中的数据，获取区块链地址
                JSONObject dataJsonObject = result.getData();
                String blockChainAddr = (String) dataJsonObject.get("native_address");
                // 使用请求中的用户ID作为区块链名称
                String blockChainName = chainProcessRequest.getUserId();
                // 构建并返回用户创建成功的响应
                return new ChainProcessResponse.Builder().data(
                        new ChainCreateData(chainProcessRequest.getIdentifier(), blockChainAddr, blockChainName,
                                chainType())).buildSuccess();
            } else {
                // 对于其他操作类型，构建处理中的响应
                return new ChainProcessResponse.Builder().responseCode(ChainCodeEnum.PROCESSING.name()).data(
                        new ChainOperationData(chainProcessRequest.getIdentifier())).buildSuccess();
            }
        }
        // 如果链操作失败，构建失败的响应
        return new ChainProcessResponse.Builder().responseCode(result.getResponseCode()).responseMessage(
                result.getResponseMessage()).buildFailed();
    }

    /**
     * 异步发送消息
     *
     * @param chainOperateInfo 消息的业务操作信息，包含业务ID、业务类型等
     * @param chainResultData  链路操作的结果数据
     */
    @Override
    public void sendMsg(ChainOperateInfo chainOperateInfo, ChainResultData chainResultData) {
        // 创建链路操作主体对象
        ChainOperateBody chainOperateBody = new ChainOperateBody();
        // 设置业务ID
        chainOperateBody.setBizId(chainOperateInfo.getBizId());
        // 设置业务类型
        chainOperateBody.setBizType(ChainOperateBizTypeEnum.valueOf(chainOperateInfo.getBizType()));
        // 设置操作信息ID
        chainOperateBody.setOperateInfoId(chainOperateInfo.getId());
        // 设置操作类型
        chainOperateBody.setOperateType(ChainOperateTypeEnum.valueOf(chainOperateInfo.getOperateType()));
        // 设置链路类型
        chainOperateBody.setChainType(ChainType.valueOf(chainOperateInfo.getChainType()));
        // 设置链路操作结果数据
        chainOperateBody.setChainResultData(chainResultData);
        // 消息监听：ChainOperateResultListener
        // 发送消息到指定的主题
        streamProducer.send("chain-out-0", chainOperateInfo.getBizType(), JSON.toJSONString(chainOperateBody));
    }


    /**
     * 执行post方法
     * @param chainRequest
     * @return
     */
    protected abstract ChainResponse doPost(ChainRequest chainRequest);

    /**
     * 执行delete方法
     * @param chainRequest
     * @return
     */
    protected abstract  ChainResponse doDelete(ChainRequest chainRequest);

    /**
     * 执行get方法
     * @param chainRequest
     * @return
     */
    protected abstract ChainResponse doGetQuery(ChainRequest chainRequest);

    public static <T, R extends ChainProcessResponse> ChainProcessResponse handle(T request, Function<T, R> function) {
        requireNonNull(request);
        BeanValidator.validateObject(request);
        return function.apply(request);
    }

    /**
     * 返回chainType
     * @return
     */
    protected abstract String chainType();
}
